<template>
  <div>
    <el-card class="box-card">
      <div slot="header" class="clearfix">
        <span>文章列表</span>
      </div>
      <!-- 搜索区域 -->
      <div class="search-box">
        <el-form :inline="true" :model="q">
          <el-form-item label="文章分类">
            <el-select
              v-model="q.cate_id"
              placeholder="请选择分类"
              size="small"
            >
              <!-- 循环渲染分类的可选项 -->
              <el-option v-for="obj in cateList" :key="obj.id" :label="obj.cate_name" :value="obj.id"></el-option> <!--这里的value值是绑定到select的model里的-->
            </el-select>
          </el-form-item>
          <el-form-item label="发布状态" style="margin-left: 15px">
            <el-select v-model="q.state" placeholder="请选择状态" size="small">
              <el-option label="已发布" value="已发布"></el-option>
              <el-option label="草稿" value="草稿"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" size="small" @click="choseFn"
              >筛选</el-button
            >
            <el-button type="info" size="small" @click="resetFn"
              >重置</el-button
            >
          </el-form-item>
          <!-- 发表文章的按钮 -->
          <el-button
          type="primary"
          size="small"
          class="btn-pub"
          @click="showPubDialogFn"
          >发表文章</el-button
        >
        </el-form>
      </div>
      <!-- 文章表格区 -->
      <el-table
        :data="artList"
        border stripe
        style="width: 100%">
        <el-table-column
          prop="title"
          label="文章标题"
          width="180">
          <template v-slot="scope">
            <el-link type="primary" @click="showDetailFn(scope.row.id)">{{ scope.row.title }}</el-link>
          </template>
        </el-table-column>
        <el-table-column
          prop="cate_name"
          label="文章分类"
          width="180">
        </el-table-column>
        <el-table-column
          prop="pub_date"
          label="发布时间">
           <template v-slot="scope">
            <span>{{ $formatDate(scope.row.pub_date) }}</span>
           </template>
        </el-table-column>
        <el-table-column
          prop="state"
          label="状态">
        </el-table-column>
        <el-table-column
          label="操作">
          <!--
            scope变量值：{
              row: 数据对象
            }
            可以解构赋值
           -->
          <template v-slot="{ row }">
            <el-button type='danger' size="mini" @click="removeFn(row.id)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- 分页区域 -->
      <div style="text-align:center">
        <el-pagination
        @size-change="handleSizeChangeFn"
        @current-change="handleCurrentChangeFn"
        :current-page.sync="q.pagenum"
        :page-sizes="[2, 3, 5, 10]"
        :page-size.sync="q.pagesize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
        </el-pagination>
      </div>
      <!-- 发表文章的 Dialog 对话框 -->
      <el-dialog
        title="发表文章"
        :visible.sync="pubDialogVisible"
        fullscreen
        :before-close="handleClose"
        @close="dialogCLoseFn"
      >
      <el-form :model="pubForm" :rules="pubFormRules" ref="pubFormRef" label-width="100px">
        <el-form-item label="文章标题" prop="title">
          <el-input placeholder="请输入标题" v-model="pubForm.title"></el-input>
        </el-form-item>
        <!-- 文章分类选择 -->
        <el-form-item label="文章分类" prop="cate_id">
          <el-select v-model="pubForm.cate_id" label="请选择分类" style="width:100%">
            <el-option v-for="obj in cateList" :key="obj.id" :label="obj.cate_name" :value="obj.id"></el-option>
          </el-select>
        </el-form-item>
        <!-- 输入文章，富文本编辑器 -->
        <!-- <el-form-item label="输入文章" prop="content">
          <quill-editor v-model="pubForm.content" @blur="onContentChange()" ></quill-editor>
        </el-form-item> -->
        <el-form-item label="输入文章" prop="content">
          <quill-editor v-model="pubForm.content" @change="onContentChange()" ></quill-editor>
        </el-form-item>
        <!-- 文章封面 -->
        <el-form-item label="文章封面" prop="cover_img">
            <!-- 用来显示封面的图片 -->
            <img
              src="../../assets/images/cover.jpg"
              alt=""
              class="cover-img"
              ref="imgRef"
            />
            <br />
            <!-- 文件选择框，默认被隐藏 -->
            <input
              type="file"
              style="display: none"
              accept="image/*"
              ref="iptFileRef"
              @change="onCoverChangeFn"
            />
            <!-- 选择封面的按钮 -->
            <el-button type="text" @click="chooseImgFn">+ 选择封面</el-button>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="pubAtricleFn('已发布')">发布</el-button>
          <el-button type="info" @click="pubAtricleFn('草稿')">存为草稿</el-button>
        </el-form-item>
      </el-form>
      </el-dialog>
      <!-- 查看文章详情对话框 -->
      <el-dialog
        title="文章预览文章"
        :visible.sync="detDialogVisible"
        :before-close="() => detDialogVisible = false"
      >
        <!-- 文章标题 -->
        <h1 style="text-align:center">{{ artDetil.title }}</h1>
        <div class="info">
          <span>作者：{{ artDetil.nickname || artDetil.username }}</span>
          <span>发布时间：{{ $formatDate(artDetil.pub_date) }}</span>
          <span>所属分类：{{ artDetil.cate_name }}</span>
          <span>状态：{{ artDetil.state }}</span>
        </div>
          <!-- elementui提供的分割线 -->
          <el-divider></el-divider>
          <!-- 文章详情 -->
          <div v-html="artDetil.content" class="detail-box"></div>
          <!-- elementui提供的分割线 -->
          <el-divider></el-divider>
           <!-- 文章封面 -->
           <img v-if="artDetil.cover_img" :src="baseURL+artDetil.cover_img" >
      </el-dialog>
  </el-card>
  </div>
</template>

<script>
import { getArtCateListAPI, uploadArticleAPI, artListAPI, getArtDetailAPI, delArtAPI } from '@/api/index'
import { baseURL } from '@/utils/request'
import defaultImg from '@/assets/images/cover.jpg'
/**
 * webpack会把图片变为一个base64字符串/或者是打包后的临时地址
 * 标签和样式中，引入图片文件直接写“静态路径”（把路径放到js的vue变量里是不行的）
 * 原因：webpack分析标签的时候，如果src是一个相对路径，他回去帮我们找到那个路径的文件并且一并打包
 * 打包的时候分析文件大小，小图转成base64字符串，是大图的话会在运行时拷贝图片换个路径给img的src显示
 *
 * vue变量中的路径赋予给标签都会被当成字符串使用
 * 以前在js里写的路径都是在能被找到真实路径的
 * 但是赋予vue里的变量的路径会随着打包一起发到内存/dist目录下，相对路径就会变化
 */
export default {
  name: 'ArtList',
  data () {
    return {
      // 查询参数对象
      q: {
        pagenum: 1,
        pagesize: 2,
        cate_id: '',
        state: ''
      },
      // 发布文章表单
      pubForm: {
        // 表单的数据对象
        title: '',
        cate_id: '',
        content: '',
        cover_img: '',
        state: ''
      },
      pubDialogVisible: false, // 控制发布文章对话框的出现或者隐藏
      detDialogVisible: false, // 文章详情对话框出现或者隐藏
      // 表单验证规则表
      pubFormRules: {
        // 表单的验证规则对象
        title: [
          { required: true, message: '请输入文章标题', trigger: 'blur' },
          {
            min: 1,
            max: 30,
            message: '文章标题的长度为1-30个字符',
            trigger: 'blur'
          }
        ],
        cate_id: [// 这里有一个问题，把cate_id改成id，prop也改成id，trigger为change，但是验证规则不消失？？？？
          { required: true, message: '请选择文章类别', trigger: 'change' }
        ],
        content: [
          { required: true, message: '请输入文章内容', trigger: 'blur' }
        ],
        cover_img: [
          { required: true, message: '请选择文章封面', trigger: 'change' }
        ]
      },
      // 保存分类
      cateList: [],
      // 保存文章列表
      artList: [], // 保存当前页
      total: 0,
      // 文章详情
      artDetil: [],
      // baseURL
      baseURL
    }
  },
  created () {
    this.getCateListFn()
    this.getArtListFn()
  },
  methods: {
    // 点击发表文章按钮显示对话框
    showPubDialogFn () {
      this.pubDialogVisible = true
    },
    // 关闭对话框
    async handleClose (done) { // done的作用，调用就会关闭对话框
      const confirmResult = await this.$confirm('点击此操作会导致信息丢失，是否继续？', '提示', { // 得到的是个Promise对象
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).catch(err => err)
      if (confirmResult === 'cancel') return
      // 确认关闭
      done()
      this.getArtListFn()
    },
    // 获取所有文章分类
    async getCateListFn () {
      const { data: res } = await getArtCateListAPI()
      this.cateList = res.data
      console.log(this.cateList)
    },
    // 获取文章列表
    async getArtListFn () {
      // console.log(this.q)
      const { data: res } = await artListAPI(this.q)
      this.artList = res.data
      this.total = res.total
      console.log(res)
    },
    // 上传封面
    chooseImgFn () {
      this.$refs.iptFileRef.click() // 用js模拟按钮点击事件
    },
    // 选择图片，显示在封面
    onCoverChangeFn (e) {
      // 拿到数组文件
      const files = e.target.files
      // console.log(files[0])
      if (files.length === 0) {
        // 没有选择图片
        this.pubForm.cover_img = null
        this.$refs.imgRef.setAttribute('src', defaultImg)
      } else {
        // 保存图片到pubForm里面
        this.pubForm.cover_img = files[0]
        // 1 、使用URL.createObjectURL()方式
        const url = URL.createObjectURL(files[0])
        // console.log(url) // blob:http://localhost:8080/75c1432d-004d-4cf9-b3a0-e0e349a162f0
        this.$refs.imgRef.setAttribute('src', url)
        // 2.使用base64字符串形式
        // const fr = new FileReader()
        // fr.readAsDataURL(files[0])// 传入文件开始读
        // fr.onload = (e) => {
        //   console.log(e.target.result)
        //     //image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASgAAADKCAYAAAAW/N0kAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAC5eSURBVHhe7d0LXBTl/j/wj1xc2BVRLqugLBdBRBERRUVEvGVeUo9aKkWmqWlWWlae6pzzz0516nSyc+rkyVIrzSQ19aeVqalpioZXQBAIEAQFXQS5yGVlwf/zzM7CLhdd5OKsft+v1+rOM8Ps7O7sd77PM8/M027AgAG3cJeKi0vEZ4SQ+9WtW7UhQv+8fXtr4X8+rX+4u7sjM/OiUN5SLMT/CSGkHsPgdC9QgCKESBZV8QghjaqbQemnW7KKFxn5BCZOnChOGaMMihDSLHWDWEuiAEUIabaWClLXrl3Dq68uR0TE48KDAhQh5K60ZuakRwGKECJZFKAIIU3U+pmTHp3FI4Q0qm41Tjd9CxqNRniun8//9/f3p7N4hJAHBwUoQohk0Fk8QojZoABFCJEsClCEEMmiAEUIkQwnJyf8618fICpqk/Aw224GhUOfQ4FbKKosbcQSQkhDLKsq4JAdjU7HVoklprvX3QzMMkDx4GQhs4VH6nZYV94QSwkhDam07oBMn2mo1pQ3OUi1RYC6HbOs4vHMiYITIabhvxP+e+G/G3NjlgGKV+soOBFiOv57McfmEGokJ4RIFgUoQohkUYAihEgWBShCiGRRgCKESBYFKEKIZFGAIoRIFgUoQohkUYAihEgWBShCiGSZ5cXCqY9vx7ADi8SpOwsPD8ekSZNgZWUlltSqqKjA119/jeTkZGGaX/A4evRouLq6QiaTCWWcWq3GmjVrkJeXJ5YQYl6Ojl4Nn03TxCnT0MXCbejy5cs4cuSI0ePYsWNC8OFGjBiBp556Cm5ubsK9kU+ePCkErtzcXFRXV8PS0lJYjhDSNh6oDIoHm7Vr14qlxhwcHPDss89CLp…
        //     // artList.vue:187
        //   this.$refs.imgRef.setAttribute('src', e.target.result)
        // }
      }
      this.$refs.pubFormRef.validateField('cover_img')
    },
    // 点击发布/存为草稿-str的值：已发布/草稿
    pubAtricleFn (str) {
      this.pubForm.state = str
      // console.log(str, this.pubForm.state)
      // console.log(this.pubForm)
      // 兜底校验，如果填写了文章信息，则通过校验
      this.$refs.pubFormRef.validate(async valid => {
        if (valid) {
          // 如果编辑器使用的是change校验，那么发布文章时编辑器改变，会再次进行校验，我们要在通过校验后关闭校验
          this.contentChangeFlag = true
          // 通过校验
          // console.log(this.pubForm)
          const fd = new FormData() // 表单数据对象容器，FormData类是HTML5新出的专门为了装文件内容和其他的参数的一个容器
          // fd.append('参数名', 值)
          fd.append('cate_id', this.pubForm.cate_id)
          fd.append('title', this.pubForm.title)
          fd.append('content', this.pubForm.content)
          fd.append('cover_img', this.pubForm.cover_img)
          fd.append('state', this.pubForm.state)
          const { data: res } = await uploadArticleAPI(fd)
          console.log(res)
          if (res.code !== 0) return this.$message.error('请输入完整的文章信息')
          this.$message.success('文章发布成功')
          this.$refs.pubFormRef.resetFields()
          this.$refs.imgRef.setAttribute('src', defaultImg)
        } else {
          return false
        }
      })
    },
    // 校验对话框表单中的content项
    onContentChange () {
      if (this.contentChangeFlag !== true) {
        this.$refs.pubFormRef.validateField('content') // 参数为要校验的prop值,这里校验编辑器用了失焦，失焦可以被resetfields()置空
      }
    },
    // 新增文章-点击对话框关闭按钮-清空表单
    dialogCLoseFn () {
      this.$refs.pubFormRef.resetFields()
      // 关闭对话框时，关闭编辑框校验，避免因为编辑框change了，而产生校验
      this.contentChangeFlag = true
      this.$refs.imgRef.setAttribute('src', defaultImg)
    },
    // 每页条数改变：也就是pageSize 发生了变化
    // 分页的核心思想就是根据选择的页码/条数，影响对象q的对于属性的值，在重新发一遍显示列表数据的请求
    // 偶发性bug：当低pageSize切换到高pageSize时，页码数会变化，这时下面两个方法都会调用，有时切换pagesize的方法
    // 比切换页码的方法耗时久一点，会出现覆盖另一个方法，导致页面不显示文章列表
    // tip：并发执行的两条网络请求，不确定谁先响应
    // 解决方法，改变每页条数时，自动切换到第一页
    handleSizeChangeFn (sizes) {
      // 为pageSize赋值
      // 因为分页的标签绑定了.sync，所以进行了双向绑定，页面改变了每页页码数，vue变量里面也会改变
      // 这里手动再写一遍，保险起见，当页码变化时也一样
      this.q.pagesize = sizes
      this.q.pagenum = 1 // 改变每页条数当前页自动回到第一页
      this.getArtListFn()
    },
    // 当前页码发生了变化
    handleCurrentChangeFn (newPage) {
      this.q.pagenum = newPage
      // 重新发起请求
      this.getArtListFn()
    },
    // 筛选按钮
    choseFn () {
      // 目的：当有了筛选条件，让页码回归1，条数回归2
      this.q.pagenum = 1
      this.q.pagesize = 2
      console.log(this.q.cate_id)
      this.getArtListFn(this.q)
    },
    // 重置按钮->每页条数和当前页码，以及筛选条件都回到初始状态
    resetFn () {
      this.q.pagenum = 1
      this.q.pagesize = 2
      this.q.cate_id = ''
      this.q.state = ''
      this.getArtListFn()
    },
    // 点击标题，显示文章详情
    async showDetailFn (artId) {
      const { data: res } = await getArtDetailAPI(artId)
      this.artDetil = res.data
      this.detDialogVisible = true
    },
    // 删除列表某项文章
    /**
     * 在这里发现一个bug
     * 当最后一页只剩一个数据，点击删除时，
     * 页码数会和当前页码会正常改变，但是没有请求数据
     * 原因：
     *  删除了此页最后一条数据，但是刷新列表请求的页码数还是当前页
     * 解决办法：
     *  判断是否是此页最后一条数据，是的话，页码减一，在请求刷新列表
     */
    async removeFn (artId) {
      const { data: res } = await delArtAPI(artId)
      if (res.code !== 0) return this.$message.error('删除文章失败')
      this.$message.success('删除文章成功')
      if (this.artList.length === 1) {
        if (this.q.pagenum > 1) { // 保证页码数至少为1
          this.q.pagenum--
        }
      }
      await this.getArtListFn()
    }
  }
}
</script>
<style lang="less" scoped>
.search-box {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  .btn-pub {
    margin-top: 5px;
  }
}

// 设置富文本编辑器的默认最小高度
// ::v-deep作用: 穿透选择, 正常style上加了scope的话, 会给.ql-editor[data-v-hash]属性, 只能选择当前页面标签或者组件的根标签
// 如果想要选择组件内的标签(那些标签没有data-v-hash值)所以正常选择选不中, 加了::v-deep空格前置的话, 选择器就会变成如下形式
// [data-v-hash] .ql-editor 这样就能选中组件内的标签的class类名了
// 被穿透的的样式前加上
/deep/ .ql-editor {
min-height: 400px;
}

// 设置图片封面的宽高
.cover-img {
  width: 400px;
  height: 280px;
  object-fit: cover;
}

.el-pagination {
  margin-top: 15px;
}

.title {
  font-size: 24px;
  text-align: center;
  font-weight: normal;
  color: #000;
  margin: 0 0 10px 0;
}

.info {
  font-size: 12px;
  span {
    margin-right: 20px;
  }
}

// 修改 dialog 内部元素的样式，需要添加样式穿透
::v-deep .detail-box {
  img {
    width: 500px;
  }
}
</style>
